home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / pascal / heap.com / HEAPLOG.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1989-03-11  |  7.4 KB  |  305 lines

  1. {*****************************************************************************
  2.  This unit logs heap activity to disk. A report is automatically generated at
  3.  program startup and exit. Additional reports may be generated at any time by
  4.  calling DumpHeapLog.
  5.  
  6.  For further information, refer to HEAP.DOC.
  7.  
  8.  Copyright (C) TurboPower Software, 1989. All rights reserved.
  9.  May be distributed freely, but not commercially without express permission
  10.  of TurboPower Software.
  11.  
  12.  Version 5.0.
  13.    First release.
  14. *****************************************************************************}
  15.  
  16. {Define the following to have HEAPLOG report the FAR return address of each
  17.  caller to GetMem and FreeMem}
  18. {.$DEFINE AllocRet}
  19.  
  20. {$R-,S-,B-,F-,I-,V-}
  21.  
  22. unit HeapLog;
  23.   {-Keep a log of heap activity}
  24.  
  25. interface
  26.  
  27. uses
  28.   GrabHeap;
  29.  
  30. const
  31.   MaxLog = 1000;              {Maximum number of blocks allocated at once}
  32.   HeapLogName = 'HEAP.LOG';   {File name where log is written}
  33.  
  34. type
  35.   LogRec =
  36.     record
  37.       PtrVal : Pointer;       {Address of heap block}
  38.       AllocSize : Word;       {Bytes allocated}
  39.       AllocAt0 : Pointer;     {First return address of GetMem or New call}
  40.       {$IFDEF AllocRet}
  41.       AllocAt1 : Pointer;     {Next return address of GetMem or New call}
  42.       {$ENDIF}
  43.     end;
  44.   LogArray = array[1..MaxLog] of LogRec;
  45.  
  46. var
  47.   Log : ^LogArray;            {Log array stored on heap}
  48.   LogFilled : Boolean;        {Set true if simultaneous pointers exceed MaxLog}
  49.  
  50. procedure GetLog(var P : Pointer; Size : Word);
  51.   {-GetMem with logging}
  52.  
  53. procedure FreeLog(var P : Pointer; Size : Word);
  54.   {-FreeMem with logging}
  55.  
  56. procedure DumpHeapLog(Msg : string);
  57.   {-Write the current heap log to a file}
  58.  
  59. procedure ClearLog;
  60.   {-Clear all entries from the log}
  61.  
  62.   {=================================================================}
  63.  
  64. implementation
  65.  
  66. const
  67.   Digits : array[0..$F] of Char = '0123456789ABCDEF';
  68. type
  69.   SO =
  70.     record
  71.       O, S : Word;
  72.     end;
  73.   FreeRec =
  74.     record
  75.       OrgPtr : Pointer;
  76.       EndPtr : Pointer;
  77.     end;
  78.   FreeList = array[0..8190] of FreeRec;
  79.   FreeListP = ^FreeList;
  80.  
  81. var
  82.   SaveExit : Pointer;
  83.  
  84.   procedure GetLog(var P : Pointer; Size : Word);
  85.     {-GetMem with logging}
  86.   type
  87.     StackRec =
  88.       record
  89.         Dummy : Word;
  90.         BP : Word;
  91.         RetAddr : Pointer;
  92.       end;
  93.   var
  94.     Index : Word;
  95.     Stack0 : StackRec absolute Index;
  96.   begin
  97.     {Let SYSTEM do the allocation}
  98.     SystemHeapControl;
  99.     GetMem(P, Size);
  100.     CustomHeapControl(@GetLog, @FreeLog);
  101.  
  102.     {Find next free log record}
  103.     for Index := 1 to MaxLog do
  104.       with Log^[Index] do
  105.         if PtrVal = nil then begin
  106.           {Unused log entry}
  107.           PtrVal := P;
  108.           AllocSize := Size;
  109.           AllocAt0 := Stack0.RetAddr;
  110.  
  111.           {$IFDEF AllocRet}
  112.           if Stack0.BP <> 0 then
  113.             {AllocAt1 ASSUMES FIRST CALL WAS FAR!}
  114.             AllocAt1 := StackRec(Ptr(SSeg, Stack0.BP-2)^).RetAddr
  115.           else
  116.             AllocAt1 := nil;
  117.           {$ENDIF}
  118.  
  119.           Exit;
  120.         end;
  121.  
  122.     {Else log table full}
  123.     LogFilled := True;
  124.   end;
  125.  
  126.   procedure FreeLog(var P : Pointer; Size : Word);
  127.     {-FreeMem with logging}
  128.   var
  129.     Index : Word;
  130.   begin
  131.     {Let SYSTEM do the deallocation}
  132.     SystemHeapControl;
  133.     FreeMem(P, Size);
  134.     CustomHeapControl(@GetLog, @FreeLog);
  135.  
  136.     {Find and free the log record}
  137.     for Index := 1 to MaxLog do
  138.       with Log^[Index] do
  139.         if PtrVal = P then begin
  140.           PtrVal := nil;
  141.           Exit;
  142.         end;
  143.   end;
  144.  
  145.   function HexW(W : Word) : string;
  146.     {-Return hex string for word}
  147.   begin
  148.     HexW[0] := #4;
  149.     HexW[1] := Digits[hi(W) shr 4];
  150.     HexW[2] := Digits[hi(W) and $F];
  151.     HexW[3] := Digits[lo(W) shr 4];
  152.     HexW[4] := Digits[lo(W) and $F];
  153.   end;
  154.  
  155.   function HexPtr(P : Pointer) : string;
  156.     {-Return hex string for pointer}
  157.   begin
  158.     HexPtr := HexW(SO(P).S)+':'+HexW(SO(P).O);
  159.   end;
  160.  
  161.   function FreeCount : Word;
  162.     {-Return the number of free list elements}
  163.   begin
  164.     if SO(FreePtr).O = 0 then
  165.       FreeCount := 0
  166.     else
  167.       FreeCount := ($10000-SO(FreePtr).O) shr 3;
  168.   end;
  169.  
  170.   function PtrDiff(H, L : Pointer) : LongInt;
  171.     {-Return the number of bytes between H^ and L^. H is the higher address}
  172.   begin
  173.     PtrDiff := ((LongInt(SO(H).S) shl 4+SO(H).O)-
  174.                 (LongInt(SO(L).S) shl 4+SO(L).O));
  175.   end;
  176.  
  177.   procedure DumpHeapLog(Msg : string);
  178.     {-Write the current heap log to a file}
  179.   var
  180.     Index : Word;
  181.     Count : Word;
  182.     FreeCnt : Word;
  183.     FP : FreeListP;
  184.     P0 : Pointer;
  185.     P1 : Pointer;
  186.     F : Text;
  187.   begin
  188.     Assign(F, HeapLogName);
  189.     Reset(F);
  190.     if IoResult = 0 then
  191.       {File already exists}
  192.       Append(F)
  193.     else
  194.       {New file}
  195.       Rewrite(F);
  196.     if IoResult <> 0 then
  197.       Exit;
  198.  
  199.     {Count the number of heap blocks allocated}
  200.     Count := 0;
  201.     for Index := 1 to MaxLog do
  202.       with Log^[Index] do
  203.         if PtrVal <> nil then
  204.           Inc(Count);
  205.     FreeCnt := FreeCount;
  206.  
  207.     {Write a message at the start of this dump}
  208.     WriteLn(F);
  209.     WriteLn(F, Msg);
  210.     WriteLn(F);
  211.     WriteLn(F, 'MemAvail: ', MemAvail);
  212.     WriteLn(F, 'MaxAvail: ', MaxAvail);
  213.     WriteLn(F, 'HeapPtr : ', HexPtr(HeapPtr));
  214.     WriteLn(F, 'HeapCnt : ', Count);
  215.     WriteLn(F, 'FreeCnt : ', FreeCnt);
  216.     WriteLn(F, 'Filled  : ', LogFilled);
  217.  
  218.     if Count <> 0 then begin
  219.       WriteLn(F);
  220.       WriteLn(F, '  Pointer   Size  Allocated at');
  221.       {           ssss:oooo  xxxxx  ssss:oooo  ssss:oooo}
  222.       for Index := 1 to MaxLog do
  223.         with Log^[Index] do
  224.           if PtrVal <> nil then begin
  225.             {Convert code addresses to relative format}
  226.             P0 := AllocAt0;
  227.             if P0 <> nil then
  228.               Dec(SO(P0).S, PrefixSeg+$10);
  229.             {$IFDEF AllocRet}
  230.             P1 := AllocAt1;
  231.             if P1 <> nil then
  232.               Dec(SO(P1).S, PrefixSeg+$10);
  233.             {$ENDIF}
  234.             WriteLn(F, HexPtr(PtrVal), '  ', AllocSize:5, '  ', HexPtr(P0)
  235.                     {$IFDEF AllocRet}
  236.                     ,'  ', HexPtr(P1)
  237.                     {$ENDIF}
  238.                     );
  239.           end;
  240.     end;
  241.  
  242.     if FreeCnt <> 0 then begin
  243.       {Write out the free list}
  244.       FP := FreePtr;
  245.       WriteLn(F);
  246.       WriteLn(F, 'Free start  Size');
  247.       {           ssss:oooo nnnnnn}
  248.       for Index := 0 to FreeCnt-1 do
  249.         with FP^[Index] do
  250.           WriteLn(F, HexPtr(OrgPtr), ' ', PtrDiff(EndPtr, OrgPtr):6);
  251.     end;
  252.  
  253.     Index := IoResult;
  254.     Close(F);
  255.     Index := IoResult;
  256.   end;
  257.  
  258.   procedure ClearLog;
  259.     {-Clear all entries from the log}
  260.   begin
  261.     LogFilled := False;
  262.     FillChar(Log^, SizeOf(LogArray), 0);
  263.   end;
  264.  
  265.   {$F+}
  266.   procedure ExitP;
  267.     {-Write the final log report}
  268.   begin
  269.     ExitProc := SaveExit;
  270.     DumpHeapLog('Final');
  271.   end;
  272.   {$F-}
  273.  
  274.   procedure DelLogFile;
  275.     {-Delete existing log file, if any}
  276.   var
  277.     I : Word;
  278.     F : file;
  279.   begin
  280.     Assign(F, HeapLogName);
  281.     Erase(F);
  282.     I := IoResult;
  283.   end;
  284.  
  285. begin
  286.   {Delete previous log file, if any}
  287.   DelLogFile;
  288.  
  289.   {Allocate the log array on the heap}
  290.   GetMem(Log, SizeOf(LogArray));
  291.  
  292.   {Clear out the log array}
  293.   ClearLog;
  294.  
  295.   {Take over heap allocation control}
  296.   CustomHeapControl(@GetLog, @FreeLog);
  297.  
  298.   {Set up to dump a final report}
  299.   SaveExit := ExitProc;
  300.   ExitProc := @ExitP;
  301.  
  302.   {Dump initial report}
  303.   DumpHeapLog('Initial');
  304. end.
  305.